Skip to content

feat(marketplace): port official 0.78.1 manage page (管理插件和技能) - #26

Merged
tyonger-stack merged 7 commits into
mainfrom
feat/marketplace-manage-page-0781
Nov 2, 2026
Merged

tyonger-stack merged 7 commits into
mainfrom
feat/marketplace-manage-page-0781

Conversation

@tyonger-stack

@tyonger-stack tyonger-stack commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

Why

Official 0.78.1 opens a second-level page from the browse dialog's 已连接 N 个 entry. This build renders that page with upstream 0.18's layout and styling. This port brings official's page over and fixes the differences measured against the running official app.

What changed

  • Adds marketplace-manage-page-0781-patch.mjs, which rewrites upstream's detail bar, hero, installed rows and skills section to official's own components. It also re-recipes the shared row, grid, section and show-all components that official uses on both pages.
  • Adds marketplace-manage-page-0781-css-patch.mjs and marketplace-manage-page-0781-official.css. The companion holds 42 rules copied verbatim from official's stylesheet, and the injector scopes every one of them to the plugins dialog.
  • Switches the auth rows to official's connect-mode labels and hides the detail-bar title until the hero scrolls out.
  • Carries official's warning and error status-badge colours. The first cut lifted the connected variant's rule only, so the warning and error badges resolved nowhere inside the dialog and rendered with the inherited text colour. The dialog's theme tokens now declare the two colours those rules read.
  • Moves the 0.75 custom-MCP form and the "已安装 / 卸载" panel out of the dialog's second-level page. Official's page carries neither, and official keeps the connector form on a page of its own, so the panel was what made this reconstruction's page differ from official's. The form moves to the settings host slot.

Scope

This covers the manage page and the four shared components it re-recipes. It leaves the shared typography primitives alone, because official's differ from upstream's across the whole app. It also moves one repo feature out of the dialog, into the settings host.

Tradeoffs

The page is re-recipeed from upstream's own components rather than rewritten. Official's component tree would copy more bytes and would need its own state plumbing, while the recipes are the part that differs in the pixels.

The uninstall action is not lost with the panel. The plugin detail page upstream already ships carries 卸载, and the desktop plugins controller keeps its own removal path.

Blast Radius

The patch rewrites view-B5Ug8wEm.js at build time and adds rules to the renderer stylesheet. The four re-recipes reach the browse page's own rows, which matches official, since it shares those recipes across both pages. The 0.75 host removal reaches the dialog's Installed section only; the settings host gains the custom-MCP form it did not have.

Verification

  • npm run check in a clean worktree gives 2694 pass, 0 fail and 34 skipped.
  • Both apps were driven over CDP against official 0.78.1 on port 9333. Every class recipe on the page matches.
  • The three status-badge variants measure the same colour on both sides: connected rgb(0, 186, 105), warn rgb(226, 134, 0), error rgb(255, 100, 99).
  • The page region's pixel diff is 7.385 percent, and both dialogs resolve to exactly 832x702 at (16,161). The different installed sets account for the bulk of it.
  • Four mutants turn the suite red: dropping the warning rule, dropping the light yellow token, dropping the dark yellow token, and taking the status variants out of the ported-class set.
  • The packaged build's app.asar sha256 b2803efb37b4fd42f9292520153550236cf9fcfc2c8dc10f551c09f6d272272b was deployed to /Applications/Grok Node.app, and the deployed asar's sha256 equals the freshly built asar's.

Tian added 2 commits November 2, 2026 04:34
The browse dialog's 已连接 N 个 entry opens a second-level page. Upstream
0.18 renders that page differently, so this ports official 0.78.1's detail
bar, hero, installed rows and skills section, and re-recipes the four
shared components official uses on both pages.

Three differences are fixed. The green 已连接 badge rendered white because
the injector looked for a class token anywhere in the stylesheet instead of
inside the dialog. The auth rows read 身份验证 because upstream hardcodes
the authorize-mode labels. The detail-bar title stayed visible where
official hides it until the hero scrolls out.

The recipe fixture is captured live from official. The CSS companion holds
official's rules verbatim.
Documents where each behaviour comes from in official's bytes, the four
defects found against official, the measured parity, and the pixel delta
with its two sources.
@tyonger-stack
tyonger-stack force-pushed the feat/marketplace-manage-page-0781 branch from e6b4f92 to ea1af35 Compare November 1, 2026 20:38
Tian added 4 commits November 2, 2026 08:58
Official 0.78.1 ships three status-badge variants — connected, warn and error —
and each carries its own colour class. The capture only ever saw the connected
variant, so the companion CSS lifted one rule of the three and the warning and
error badges shipped colourless.

The guard now walks all three variants instead of the connected one alone, and a
second guard asserts that every token the ported rules read is declared on the
dialog, because an undeclared var() is invalid at computed-value time and the
rule falls back to its initial value without failing anything else.

The status variant classes move into one exported constant so the patch's
replacement text and the guard read the same source.

Fails on the current tree: "the companion CSS must lift official's warn badge
rule".
Official 0.78.1 ships three status-badge variants — connected, warn and error —
and each carries its own colour class. The capture only ever saw the connected
variant, so the companion CSS lifted one rule of the three: the warning and the
error badge resolved nowhere inside the plugins dialog and rendered with the
inherited text colour.

The companion now lifts official's warning and error rules verbatim, and the
dialog's theme tokens declare the two colours those rules read. Upstream 0.18's
palette defines neither, and the 0.75.5 group-author CSS declares both under
.rg0755-colors, a scope the dialog is not inside.

The three variant classes move into the ported-class set, so the build now
fails closed when one of them resolves nowhere instead of shipping a
colourless badge.

Official's values: light #aa6300 and #cd2834, dark #e28600 and #ff6463. The
light column is official's own :root,.sand-1vbcnu7 palette block, the dark
column is the running official app's cursor-dark theme.
The second-level page's Installed section is the 0.75.0 wave's host for the
repo's "已安装 / 卸载" panel and the custom-MCP form. Official 0.78.1's
second-level page carries neither, and official keeps the connector form on a
page of its own (qLe/7o, chunk-user-custom-connector-M5UeTEiw.js). The panel
is therefore what made this reconstruction's page differ from official's.

It also broke the build. The second-level port rewrites the section's children
from J.map to (RM0781ManageRowElements=U.map, so the host's anchor no longer
occurred and the pack step failed with "Expected one settings panel and one
plugins list for the 0.75 hosts".

The plugins host goes away. The custom-MCP form moves to the settings host
slot, which is a reconstruction surface rather than an official-parity one. The
uninstall action is not lost: the plugin detail page upstream already ships
carries 卸载, and the desktop plugins controller keeps its own removal path.

The content is taken from the sibling session's working tree, which had already
made this change.
The guard read the whole token stylesheet, so a token declared only in the dark
block counted as declared and the light theme fell back to the initial value
without anything going red. Dropping the light yellow token survived the
mutation check because the dark one was still there.

The guard now reads the light block and the dark block separately: a token the
ported rules read must be declared in the light block, and a token whose
official value differs between the two themes must be declared in the dark
block too.

The port record gains the badge-colour measurements on both running apps and the
0.75 host removal.
@tyonger-stack

Copy link
Copy Markdown
Owner Author

PASS+NOTES

Verified at head 0b548c2 in an isolated worktree (wt-verify-26, Node 26.5.1 at /Users/Apple/Documents/grokbot/.tools/node-26/bin/node, real node_modules / src/app/dist copies - no symlinks). The worktree was left clean (git status --porcelain empty); the main checkout was not touched.

1. npm run check - PASS

typecheck + source:typecheck + npm test all green, exit 0:

tests 2728
pass 2694
fail 0
cancelled 0
skipped 34
todo 0

Re-run after the mutation experiments: identical (2728 / 2694 / 0 / 34). All 34 skips are pre-existing environment skips (absent 0.75.0 snapshot, absent playwright-core, absent official comparison artifacts) - none are in this PR's scope. tests/marketplace-manage-page-0781.test.mjs runs 5/5 with 0 skipped.

2. The package builds - PASS (the step CI cannot see)

Marketplace browse chrome: official 0.78.1 chip sections + recommended section added (view-B5Ug8wEm.js).
Marketplace manage page: official 0.78.1's second-level page applied (view-B5Ug8wEm.js).
Marketplace manage-page CSS: official 0.78.1 page rules added (index-lCyB53CO.css, 23 of 42 appended; 42 classes lifted).
Marketplace detail view: official 0.78.1 plugin detail page added (view-B5Ug8wEm.js).
Marketplace detail view CSS: official 0.78.1 rules added (index-lCyB53CO.css, 8 of 32 appended).
0.75 hosts: vault card, memory import and the custom MCP form mounted into index-BlqerJhg.js
Fidelity hybrid ASAR ready: .../.build/fidelity/app.asar
Packaged application: .../dist/Grok Node.app (6 native manifest entries, 773 unpacked runtime files)
=== EXIT=0 ===

No Expected one settings panel and one plugins list for the 0.75 hosts. The 0.75 host line now names only index-BlqerJhg.js (settings) - one chunk, no plugins chunk.

Additionally: all 102 staged renderer chunks pass node --check under Node 26.5.1, and the staged chunks contain 0 occurrences of the double-wrapped "(RLocT( pattern recorded in AGENTS.md.

3. The shipped stylesheet carries the three badge rules and the tokens they read - PASS

In .build/fidelity/app/dist/renderer/assets/index-lCyB53CO.css:

:where(.sand-plugins-dialog) .sand-132xtu3:not(#\#):not(#\#):not(#\#){color:var(--sand-text-yellow-primary)}   PRESENT
:where(.sand-plugins-dialog) .sand-wtpwev:not(#\#):not(#\#):not(#\#){color:var(--sand-text-red-primary)}      PRESENT
:where(.sand-plugins-dialog) .sand-1ufr4hg:not(#\#):not(#\#):not(#\#){color:var(--sand-text-green-primary)}   PRESENT

Dialog token blocks (.sand-plugins-dialog{...} and html[data-theme="cursor-dark"] .sand-plugins-dialog{...}):

token light dark
--sand-text-yellow-primary #aa6300 #e28600
--sand-text-red-primary #cd2834 #ff6463
--sand-text-green-primary #00854a #00ba69

Cross-checked against official's own bytes, not the prose:

  • official's light palette :root,.sand-1vbcnu7 in rea-grokbot-0781/app/dist/renderer/assets/index-Dh7EWA8t.css declares --sand-text-yellow-primary:#aa6300, --sand-text-red-primary:#cd2834, --sand-text-green-primary:#00854a - exact match.
  • official's three rules appear there byte-for-byte, e.g. .sand-132xtu3:not(#\#):not(#\#):not(#\#){color:var(--sand-text-yellow-primary)}. The port's rule text is official's; the only addition is the :where(.sand-plugins-dialog) scope prefix, and :where() contributes no specificity, so the cascade is unchanged.
  • the dark pair is official's palette scale: yellow/3 = #aa6300 (light) / #e28600 (dark) and red/3 = #cd2834 (light) / #ff6463 (dark), in index.eager-common-DbK3Oi_r.js and app/dist/electron-main/main-core.cjs.
  • official's registry chunk index.eager-app-D1juOWZp.js names the same three classes: statusConnected:{kMwMTN:"sand-1ufr4hg"}, statusWarn:{kMwMTN:"sand-132xtu3"}, statusError:{kMwMTN:"sand-wtpwev"} - identical to MARKETPLACE_MANAGE_PAGE_0781_STATUS_VARIANT_CLASSES and to the built chunk.
  • the docs' colour table converts exactly: #00ba69 to rgb(0, 186, 105), #e28600 to rgb(226, 134, 0), #ff6463 to rgb(255, 100, 99).

All 42 rules in scripts/lib/marketplace-manage-page-0781-official.css were independently checked to be byte-verbatim lifts of official's rules (selector minus the scope prefix + declaration body), including the two @container sand-plugin-rows (width<560px) rules (official splits them across two identical blocks; the port merges them - equivalent) and the @media (prefers-reduced-motion:reduce) rule.

4. The 0.75 plugins host is gone, the form moved, uninstall survives - PASS

  • RPort0750PluginsPack: 0 occurrences in every built chunk, including view-B5Ug8wEm.js; the old anchor labelledBy:w,children:J.map is gone from the plugins chunk too.
  • the settings chunk index-BlqerJhg.js still mounts the custom-MCP form (MCP Servers, sand-plugins-port-0750, RPort0750SettingsPack present).
  • bridge.mcp.uninstallPlugin survives at frontend/src/recovered/features/plugins/overlay/desktop.ts:561, on a live path (removePluginBrowserItem called from desktop-surface.tsx:264), not merely declared.

5. The guards bite - PASS

Each mutant applied alone, then restored from a distinct backup; worktree re-verified clean afterwards.

mutant expected observed
remove the sand-132xtu3 rule line from marketplace-manage-page-0781-official.css "must lift official's warn badge rule" AssertionError: the companion CSS must lift official's warn badge rule - 1 fail, exit 1
remove --sand-text-yellow-primary:#aa6300; from marketplace-dialog-0781-theme-tokens.css a message about the light block AssertionError: these tokens are read by the ported rules but not declared in the dialog's light block..., actual ['--sand-text-yellow-primary'] - 1 fail, exit 1
remove the 3-line MARKETPLACE_MANAGE_PAGE_0781_STATUS_VARIANT_CLASSES loop from marketplace-manage-page-0781-css-patch.mjs the test must fail again Error: Marketplace manage-page CSS patch: the companion CSS carries rules for classes the port does not apply: sand-132xtu3, sand-wtpwev. - 1 fail, exit 1
bonus (the doc claims a 4th) remove the dark --sand-text-yellow-primary:#e28600; dark-block message AssertionError: these tokens differ between official's two themes, so the dialog's dark block must declare them... - 1 fail, exit 1

6. Nothing unrelated rode along - PASS

git diff 6333d5bf2a2117b988dcf0454b9019bdca56ee50..0b548c224bef19619f21ec9d6e1e09f2d131fd2b --stat = 15 files, exactly the 9 the port owns plus the 6 0.75 host files:

docs/MARKETPLACE-0781-PORT.md                                        +221
frontend/src/extensions/port-0750-packaged-hosts.tsx                 +15/-22
frontend/src/recovered/features/plugins/overlay/desktop-surface.tsx    -5
frontend/src/recovered/features/plugins/overlay/port-0750-extras.tsx  +18/-13
scripts/clean-build.mjs                                               +13
scripts/lib/marketplace-dialog-0781-theme-tokens.css                   +8/-2
scripts/lib/marketplace-manage-page-0781-css-patch.mjs               +619
scripts/lib/marketplace-manage-page-0781-official.css                +118
scripts/lib/marketplace-manage-page-0781-patch.mjs                   +758
scripts/lib/port-0750-renderer-patch.mjs                             +13/-20
tests/fixtures/official-0781-manage-page-recipes.json                +109
tests/fixtures/official-0781-marketplace-theme-tokens.json            +8
tests/marketplace-manage-page-0781.test.mjs                          +411
tests/plugins-port-0750-extras.test.mjs                               +4/-14
tests/port-0750-renderer-patch.test.mjs                              +10/-30

Nothing else. The evidence chain also holds up: every official chunk the port cites exists in rea-grokbot-0781, and official's message ids iSLIjg/gcoiFh/6gRgw8 carry Connect/连接, Reconnect/重新连接, Retry/重试 in index.eager-common-DbK3Oi_r.js and chunk-core-CXBwRBzw.js, with official's K chain (idle to iSLIjg, waiting to gcoiFh, else 6gRgw8) in chunk-plugin-detail-sharepoint-write-MAPmM3e9.js matching what the port reproduces. All 573 class tokens in the recipes fixture trace to official's own assets (the single apparent exception, ui-dialog--xxl, is official's runtime-built ui-dialog--${size} modifier).


NOTE (low severity - cosmetic, keyboard-focus only): one of the 42 lifted rules does not ship

The build appends "23 of 42"; the other 19 are skipped because the class already resolves inside the dialog, and the patch's rule is "the class is the identity, not the rule text". For 18 of those 19, the rule that already applies is byte-identical to official's. One is not:

  • port / official: :where(.sand-plugins-dialog) .sand-1wpm9rq:focus-visible:not(#\#):not(#\#):not(#\#){outline-color:var(--sand-border-focus-primary)}
  • what actually applies (pre-existing, unscoped, from the repo's 0.75.8 info-pane CSS): .sand-1wpm9rq:focus-visible:not(#\#):not(#\#):not(#\#){outline-color:var(--sand-border-focus)}

On the dialog subtree --sand-border-focus is initial - deliberately, because official does not define it - so outline-color is invalid at computed-value time and falls back to currentcolor: a visible focus ring where official renders transparent (official forces --sand-border-focus-primary: transparent !important under :root:not([data-sand-keyboard-nav])) or #1e86fc with keyboard nav on.

Impact: the :focus-visible outline colour of the detail bar's back button on the plugins dialog's second-level page, keyboard users only. It does not touch the three badge rules, the token declarations, the 0.75 host removal, or anything else in the port, and it is not a regression introduced here - the unscoped repo rule predates this PR; the port simply declines to lift official's rule on top of it.

Worth noting the companion CSS's header comment asserts the 42 "do not resolve inside the dialog", which is not true for sand-1wpm9rq, and the PR's guard only asserts "the rule exists in official's stylesheet" plus "the class resolves in-dialog", so this substitution is not covered. A guard asserting the lifted rule text itself reaches the staged stylesheet would close it.

The independent verifier found that the second-level page's back button draws a
pale ring on keyboard focus where official draws a blue one, and measured it on
both running apps: official rgb(24, 132, 252), this build rgba(245, 245, 245,
0.66), both 2 px solid.

The cause is two earlier waves, not this port: the app-link-card port writes an
unscoped `.sand-1wpm9rq:focus-visible{outline-color:var(--sand-border-focus)}`,
and the dialog resets `--sand-border-focus` to `initial` because official does
not define it. Official's own rule for that class is skipped by the
marketplace companions, because the app-link rule makes the class look already
resolved.

Official's blue reaches the button through a `[data-sand-tone="dark"]` subtree
in its runtime theme stylesheet, while the dialog element itself computes
`transparent`, so declaring the blue on the dialog would make every ring in the
dialog blue instead of only that subtree's. That is its own wave.
@tyonger-stack
tyonger-stack merged commit a549e37 into main Nov 2, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant